/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.services;

import org.braintrain.domain.ContentBase;
import org.braintrain.domain.Module;
import org.braintrain.domain.Navigation;
import org.braintrain.services.dao.NavigationDAO;

import java.util.ArrayList;
import java.util.List;

/**
 * The Navigation Service.  This Class defines the various functionality
 * commonly used when dealing with Navigation.
 * 
 * @author John Ericksen
 *
 */
public class NavigationServiceImpl implements NavigationService {
    
    private NavigationDAO navigationDAO;
    
    public void setNavigationDAO(NavigationDAO navigationDAO) {
        this.navigationDAO = navigationDAO;
    }
    
    public NavigationDAO getNavigationDAO() {
        return navigationDAO;
    }
    
    /**
     * Delete a navigation with the given Id.  This consists of 
     * completely removing the "pending" changes to the navigation item
     * as well as putting the "released" navigation (if it exists) into a
     * deleted NavigationState.  This "deleted" Navigation will be deleted when
     * the Module is released. 
     * 
     * @param id
     */
    public void deleteNavigation(Long id) {
        Navigation nav = ((Navigation)navigationDAO.getById(Navigation.class, id));
        
        this.navigationDAO.removeDeltaNavigationFromChain(nav);
        
        nav.setDeleted(true);
        
        if(nav.getNullSafeContent().getDeltaValue() != null)
        {
            ContentBase delta = nav.getNullSafeContent().getDeltaValue();
            nav.getNullSafeContent().setDeltaValue(null);
            this.navigationDAO.delete(delta);
        }
        
        if(nav.isPending()){
            this.navigationDAO.delete(nav);
        }        
    }

    /**
     * Returns a List of List of Navigations.  This function finds all the "pending" (if delta = true) "link/popup" pages
     * that exist in the module (where mainNavigaton = false) and resolves the chain of navigations
     * after the root elements.  Also, deleted items not
     * along the getDeltaNext() path that exist along the getNext() path are added in their respective
     * places (if includeDeletes = true).
     * @param module
     */
    public List<List<Navigation>> getLinkedChains(Module module, boolean delta, boolean includeDeletes) {
        
        if(module.getId() != null)
        {
            return this.navigationDAO.getLinkNavigationChains(module, delta, includeDeletes);
        }
        else{
            return new ArrayList<List<Navigation>>();
        }
    }
    
    /**
     * Returns a List of Navigations.  This function finds all the "Main" pages
     * that exist in the module (where mainNavigaton = true) and resolves the chain of navigations
     * after the root element that exist along the getNext() links.
     * @param module
     */
    public List<Navigation> getMainNavigationChain(Module module) {
        
        if(module.getId() != null)
        {
            //this.navigationDAO.clearSession();
            return this.navigationDAO.getMainNavigationChain(module, false, true);
        }
        return new ArrayList<Navigation>();
    }

    
    /**
     * Returns a List of Navigations.  This function finds all the "Main" pages
     * that exist in the module (where mainNavigaton = true) and resolves the chain of navigations
     * after the root element that exist along the getDeltaNext() links.  Also, deleted items not
     * along the getDeltaNext() path that exist along the getNext() path are added in their respective
     * places.
     * @param module
     */
    public List<Navigation> getMainDeltaNavigationChain(Module module, boolean includeDeleted) {
        
        
        if(module.getId() != null)
        {
            //this.navigationDAO.clearSession();
            return this.navigationDAO.getMainNavigationChain(module, true, includeDeleted);
        }
        return new ArrayList<Navigation>();
    }
    
    /**
     * Inserts a new Navigation into the navigation chain.  This links the given "nav"
     * released.
     * @param current
     * @param nav
     */
    public Navigation insertNavigation(Navigation current, Navigation nav) {
        return navigationDAO.insertNavigationNext(current, nav);
    }

    /**
     * Performs a swap with the getDeltaPrev() item.  Also, sets the moved flag
     * to true.
     */
    public void saveMoveNavigationBack(Long id) {
        Navigation nav = (Navigation)navigationDAO.getById(Navigation.class, id);
        nav.setMoved(true);
        nav.getDeltaPrev().setMoved(true);
        this.navigationDAO.swapWithNext(nav.getDeltaPrev());
        
        this.navigationDAO.clearSession();
        //this.navigationDAO.saveOrUpdate(nav);
    }

    /**
     * Performs a swap with the getDeltaNext() item.  Also, sets the moved flag
     * to true.
     */
    public void saveMoveNavigationForward(Long id) {
        Navigation nav = (Navigation)navigationDAO.getById(Navigation.class, id);
        nav.setMoved(true);
        nav.getDeltaNext().setMoved(true);
        this.navigationDAO.swapWithNext(nav);
        
        this.navigationDAO.clearSession();
        //this.navigationDAO.saveOrUpdate(nav);
    }

    /**
     * Saves and releases a given module.  This consists of copying the pending Navigation
     * into the getNext() and getPrev() chain as well as deleting "deleted" or unlinked
     * Navigators.  Of course, Tests are a special case because they cannot be deleted
     * per the TestLog.
     */
    public void saveRelease(Module module){
        
        this.navigationDAO.saveDeltaNavigationChanges(module);
        
        module.getNullSafeName().publish();//setName(module.getName().getDeltaValue());
        module.getNullSafeDescription().publish();//setDescription(module.getDeltaDescription());
        module.getNullSafeHeader().publish();//setHeader(module.getDeltaHeader());
        module.getNullSafeFooter().publish();//setFooter(module.getDeltaFooter());
        
        module.setActive(true);    
        
        this.navigationDAO.saveOrUpdate(module);
    }

    /**
     * Removes all changes from the chain of getDeltaNext().  This can be disasterous if
     * used at a bad time (like when a module hasn't been released yet.
     */
    /*public void saveSyncDeltaWithReleased(Module module) {
        if(module != null)
        {
            this.navigationDAO.saveSyncDeltaWithReleased(module);
            //header, footer
            module.getHeader().publish();//setDeltaHeader(module.getHeader());
            module.getFooter().publish();//setDeltaFooter(module.getFooter());
            
            //name, description
            module.getName().publish();//setDeltaName(module.getName());
            module.setDeltaDescription(module.getDescription());
            
            this.navigationDAO.saveOrUpdate(module);
        }
    }*/

    /**
     * Swaps two navigation elements using the robust swap method
     * (verses the swapWithNext function that swaps two elements next to each other)
     */
    public void saveSwapNavigation(Navigation released, Navigation edited) {
        this.navigationDAO.swap(released, edited);
    }

    /**
     * As long as there is a pending change to a released content and
     * the pending navigation points to the released navigation, this
     * function will delete the current changes and swap back in the released
     * changes.
     */
    public void saveRollBackDeltaNavigation(Long navigationId) {
        Navigation navigation = (Navigation)navigationDAO.getById(Navigation.class, navigationId);
        if(navigation.getNullSafeContent().getDeltaValue() != null && navigation.getNullSafeContent().getValue() != null)
        {
            ContentBase delta = navigation.getNullSafeContent().getDeltaValue();
            navigation.getNullSafeContent().setDeltaValue(null);
            this.navigationDAO.delete(delta);
            
            navigation.setPending(false);
            
            this.navigationDAO.saveOrUpdate(navigation);
        }
    }
    
    /**
     * Undelete a given Navigation.  This reconnects the navigation to its previous 
     * non-deleted element.  If one don't exist, then reconnect it to the next non-deleted
     * element.  Of course, the respective deltaNext is null.
     */
    public void saveUndeleteNavigation(Long navigationId)
    {
        Navigation navigation = (Navigation)navigationDAO.getById(Navigation.class, navigationId);
        if(navigation != null && navigation.isDeleted())
        {
            Navigation reconnectNav = navigation.getPrev();
            while(reconnectNav != null && reconnectNav.isDeleted())
            {
                reconnectNav = reconnectNav.getPrev();
            }
            
            //if reconnectNav reached the end of the previous chain and found no non-deleted items
            //look for non-deleted items after the given navigation
            if(reconnectNav == null)
            {
                reconnectNav = navigation.getNext();
                while(reconnectNav != null && reconnectNav.isDeleted())
                {
                    reconnectNav = reconnectNav.getNext();
                }
                if(reconnectNav != null)
                {
                    while(reconnectNav.getDeltaPrev() != null)
                    {
                        reconnectNav = reconnectNav.getDeltaPrev();
                    }
                    this.navigationDAO.insertNavigationPrev(reconnectNav, navigation);
                }
            }
            else if(!reconnectNav.isDeleted())
            {
                this.navigationDAO.insertNavigationNext(reconnectNav, navigation);
            }
            
            navigation.setDeleted(false);
            navigation.setMoved(false);
            navigation.setPending(false);
            
            //this.navigationDAO.saveOrUpdate(navigation);
        }
    }
}
